#pragma once

#include "Win32Utils.h"

#include <Windows.h>
#include <hidsdi.h>
// wil should be included after system API headers
#include "wil/resource.h"

#include <memory>
#include <vector>


namespace TLAC::Input::RawInputDetails
{
	/* Represents a Raw Input HID device. Encapsulates related resources and APIs. */
	class HidDevice {
	public:
		/* From a handle to a Raw Input device, obtain various device info used to parse
		and construct reports, and open a file handle for writing output reports. */
		bool OpenHandle(HANDLE handle);

		/* Call HidP_GetSpecificButtonCaps on this device. */
		std::vector<HIDP_BUTTON_CAPS> GetSpecificButtonCaps(
			HIDP_REPORT_TYPE     ReportType,
			USAGE                UsagePage,
			USHORT               LinkCollection,
			USAGE                Usage);
		/* Call HidP_GetSpecificValueCaps on this device. */
		std::vector<HIDP_VALUE_CAPS> GetSpecificValueCaps(
			HIDP_REPORT_TYPE     ReportType,
			USAGE                UsagePage,
			USHORT               LinkCollection,
			USAGE                Usage);
		/* Call HidP_GetData on this device with ReportType set to HidP_Input.
		This function uses an internal buffer to reduce memory allocation.
		The returned const reference is guaranteed valid until
		any member function is called or the HidDevice object is destructed.
		If not using the return value "immediately", make copies. */
		const std::vector<HIDP_DATA>& GetDataFromInputReport(PCHAR Report, ULONG ReportLength);
		/* Call HidP_InitializeReportForID on this device. */
		bool InitializeReportForID(HIDP_REPORT_TYPE ReportType, UCHAR ReportID, std::vector<CHAR>& Report);
		/* Call HidP_SetData on this device with ReportType set to HidP_Output. */
		bool SetDataForOutputReport(std::vector<HIDP_DATA>& data_list, std::vector<CHAR>& report);

		/* Return the opened file handle for the device, which can be used to write output reports. */
		HANDLE GetFileHandle() {
			return file_.get();
		}
	private:
		unique_hidp_preparsed_data preparsed_data_;
		std::unique_ptr<HIDP_CAPS> capabilities_;

		std::vector<HIDP_DATA> input_data_buffer_;
		std::size_t input_data_buffer_maxlen_;

		wil::unique_hfile file_;
	};

	class AsyncWrite {
	public:
		AsyncWrite() {
			ov_.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
		}

		/* Write the contents of *buffer* to *file* asynchronously.
		Make sure this object does not hold a pending write before calling this, and
		do not change the contents of buffer until the write completes. 
		Returns true on success, false on error. */
		bool Write(HANDLE file);

		/* Check whether there is a pending asynchronous write.
		Returns true if there is a pending write, false if there is not.*/
		bool IsPending();

		std::vector<CHAR> buffer{};
	private:
		unique_overlapped ov_{};
		bool write_pending_{ false };
	};
}